#include <iostream>
#include <fstream>

using namespace std;

#include "types.h"
#include "helper.h"
#include "base64.h"

static const string b64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

// convert base64 to ASCII
string base64::decodeBase64(string input)
{
	uint nofchars, index = 0; 
	uint i = 0, j;
	string output;
	uchar b64chars[4], asciichars[3];

	nofchars = input.length();
	while((nofchars--) && (input[index] != '='))
	{
		if (!isBase64(input[index]))
		{
			index++;
			continue;
		}
			
		b64chars[i] = input[index++];
		b64chars[i] = b64_chars.find(b64chars[i]);
		i++;
		if (i == 4)
		{
			asciichars[0] = (b64chars[0] << 2) + ((b64chars[1] & 0x30 ) >> 4);
			asciichars[1] = ((b64chars[1] & 0x0f) << 4) + ((b64chars[2] & 0x3c) >> 2);
			asciichars[2] = ((b64chars[2] & 0x03) << 6) + b64chars[3];

			for (i = 0; i < 3; i++)
			{
//				cout << asciichars[i];
				output += asciichars[i];
			}
			i = 0;
		}
	}

	if (i)
	{
		switch (i)
		{
			case 3:
				asciichars[1] = ((b64chars[1] & 0x0f) << 4) + ((b64chars[2] & 0x3c) >> 2);
			case 2:
				asciichars[0] = (b64chars[0] << 2) + ((b64chars[1] & 0x30 ) >> 4);
		}
	
		for (j = 0; j < i-1; j++)
		{
//			cout << asciichars[j];
			output += asciichars[j];
		}
	}
//	cout << endl;
	return output;
}

	
// Convert ASCII to base64.
string base64::encodeBase64(string input)
{
	uint len, padbytes = 0, paddedlen = 0, i = 0, index = 0;
	string output;
	uchar b64chars[4], asciichars[3];

	len = input.length();
//	cout << "original length : "<< len << endl;
	while (len % 3) 
	{
		input += '\0';
		len++;
		padbytes++;
	}

	paddedlen = len;
	//cout << "new len: "<< paddedlen << " pad: " << padbytes<<endl;
	while (len--)
	{
		asciichars[i++] = input[index++];

		if (i == 3)
		{
			b64chars[0] = (asciichars[0] & 0xfc) >> 2;
			b64chars[1] = ((asciichars[0] & 0x03) << 4) + ((asciichars[1] & 0xf0) >> 4);
			b64chars[2] = ((asciichars[1] & 0x0f) << 2) + ((asciichars[2] & 0xc0) >> 6);
			b64chars[3]	= (asciichars[2] & 0x3f);

			for (i = 0; i < 4; i++)
			{
				b64chars[i] = b64_chars[b64chars[i]];
				output.push_back(b64chars[i]);
				//cout << b64chars[i];
			}
			i = 0;
		}
	}
	if (padbytes)
	{
		paddedlen = (paddedlen*4)/3;
		paddedlen--;
		while (padbytes --)
			output[paddedlen--] = '=';
	}
	return output;
}



bool base64::decodeKeyFromBase64(const char *in, const char *out)
{
	int length;
 	uchar buffer[8096];

	ifstream is(in, ios::binary);
	if (!is.is_open())
	{
		cout << "Error opening input file: " << in << endl;
		return FALSE;
	}

	ofstream os (out , ios::binary| ios::app);
	if (!os.is_open())
	{
		cout << "Error opening output file: " << out << endl;
		return FALSE;
	}

 	// get length of file:
 	is.seekg (0, ios::end);
 	length = is.tellg();
 	is.seekg (0, ios::beg);

	// read data as a block:
	is.read ((char *)buffer,length);
	is.close();

	string str((char *)buffer, length);

//	cout << "original: " << endl << str;
	str = removeBegindAndEndLines(str);	
//	cout << "new : " << endl << str << endl;

	string inascii;
	inascii = decodeBase64(str);
//	cout << "inascii :" << inascii << endl;
	os << inascii;
	
	os.close();

	return TRUE;
}
	
bool base64::encodeKeyToBase64(const char *in, const char *out)
{

	int length;
 	uchar buffer[8096];

	ifstream is(in, ios::binary);
	if (!is.is_open())
	{
		cout << "Error opening input file: " << in << endl;
		return FALSE;
	}

	ofstream os (out , ios::binary| ios::app);
	if (!os.is_open())
	{
		cout << "Error opening output file: " << out << endl;
		return FALSE;
	}

 // get length of file:
 is.seekg (0, ios::end);
 length = is.tellg();
 is.seekg (0, ios::beg);

	// read data as a block:
	is.read ((char *)buffer,length);
	is.close();

	string inbase64 = encodeBase64(string((char *)buffer, length));
	//cout << inbase64 << endl;
		
	os << priv_key_start1 << endl;

	for (uint i = 0; i < inbase64.size(); i += 64)
		os << inbase64.substr(i, 64).c_str() << endl;

	os << priv_key_end1 << endl;
	os.close();

	return TRUE;
}


string base64:: removeBegindAndEndLines(string str)
{
	uint index = string::npos;
	index = str.find(priv_key_start1, 0);
	if (index != string::npos)
		str.replace(index, priv_key_start1.size(), priv_key_start1.size(), '*');
	
	index  = string::npos;
	index = str.find(priv_key_start2, 0);
	if (index != string::npos)
		str.replace(index, priv_key_start2.size(), priv_key_start2.size(), '*');

	index  = string::npos;
	index = str.find(priv_key_end1, 0);
	if (index != string::npos)
		str.replace(index, priv_key_end1.size(), priv_key_end1.size(), '*');
	
	index  = string::npos;
	index = str.find(priv_key_end2, 0);
	if (index != string::npos)
		str.replace(index, priv_key_end2.size(), priv_key_end2.size(), '*');
	
	index  = string::npos;
	index = str.find(pub_key_start, 0);
	if (index != string::npos)
		str.replace(index, pub_key_start.size(), pub_key_start.size(), '*');
	
	index  = string::npos;
	index = str.find(pub_key_end, 0);
	if (index != string::npos)
		str.replace(index, pub_key_end.size(), pub_key_end.size(), '*');
	
	index  = string::npos;
	index = str.find(cert_start, 0);
	if (index != string::npos)
		str.replace(index, cert_start.size(), cert_start.size(), '*');
	
	index  = string::npos;
	index = str.find(cert_end, 0);
	if (index != string::npos)
		str.replace(index, cert_end.size(), cert_end.size(), '*');
	
	return str;
}	
